#include "stdafx.h"
#include "Polynomial.h"
#include <assert.h>


int Polynomial::GetDegree() const
{
	return mDegree;
}

void Polynomial::GetValuesOfBaseFunctions(float inX, int inBufferLength,
	float* ioBuffer) const
{
	assert(mDegree + 1 <= inBufferLength);
	float previous = 0;
	float current = ioBuffer[0] = 1;
	for (int n = 1; n <= mDegree; ++n)
	{
		ioBuffer[n] = ((2 * n + 1) * inX * current - (n + 1) * previous) / n;
		previous = current;
		current = ioBuffer[n];
	}
}

COLORREF Polynomial::CalculateColor(int inBuffersLength, 
	const float* inBufferX, const float* inBufferY) const
{
	assert(mDegree + 1 <= inBuffersLength);

	using namespace DirectX;
	XMVECTOR color = XMVectorZero();
	for (int n = 0; n <= mDegree; ++n)
	{
		float Px = inBufferX[n];
		for (int m = mDegree - n; m >= 0; --m)
		{
			color += (Px * inBufferY[m]) * mA[n][m];
		}
	}
	color = XMVectorClamp(color, XMVectorZero(), XMVectorSet(255, 255, 255, 0));
	int r = ::lround(XMVectorGetX(color));
	int g = ::lround(XMVectorGetY(color));
	int b = ::lround(XMVectorGetZ(color));
	return RGB(r, g, b);
}

DirectX::XMVECTOR Polynomial::CalculateColorComponents(float inX, float inY) const
{
	float bufferX[kMaxDegree + 1];
	float bufferY[kMaxDegree + 1];
	GetValuesOfBaseFunctions(inX, kMaxDegree + 1, bufferX);
	GetValuesOfBaseFunctions(inY, kMaxDegree + 1, bufferY);

	using namespace DirectX;
	XMVECTOR color = XMVectorZero();
	for (int n = 0; n <= mDegree; ++n)
	{
		float Px = bufferX[n];
		for (int m = mDegree - n; m >= 0; --m)
		{
			color += (Px * bufferY[m]) * mA[n][m];
		}
	}
	return color;
}

// ====================================

PolynomialCalculator::PolynomialCalculator()
	: mDegree(0)
{
}

void PolynomialCalculator::SetDegreeAndSide(int inDegree, int inSide)
{
	assert(inDegree <= kMaxDegree);
	assert(inSide <= kMaxBitmapSide);
	if (mDegree == inDegree && mSide == inSide)
	{
		return;
	}

	mDegree = inDegree;
	mSide = inSide;

	// calculate mDeltaF;

	// converts interval i=(0...mSide) to interval x=(-1...+1)
	const float scale = 2.0f / mSide; 

	for (int i = 0; i <= mSide; ++i)
	{
		float x = -1 + scale * i;
		float q = 1 - x * x;
		mDeltaF[0][i] = 0.25f * x * (2.0f + q);
		float prev = 0;
		float current = -3.0f * q * q;
		mDeltaF[1][i] = (5.0f / 48.0f) * current;
		for (int n = 2; n <= mDegree; ++n)
		{
			float next = ((2 * n + 1) * x * current - (n + 2) * prev) / (n - 1);
			mDeltaF[n][i] = (2 * n + 3) / (2.0f * n * (n+1) * (n+2) * (n+3)) * next;
			prev = current;
			current = next;
		}
	}
	for (int n = 0; n <= mDegree; ++n)
	{
		float valuePlusOne = mDeltaF[n][mSide];
		for (int i = mSide; i--;)
		{
			float oldValue = mDeltaF[n][i];
			mDeltaF[n][i] = valuePlusOne - oldValue;
			valuePlusOne = oldValue;
		}
	}
}

int PolynomialCalculator::GetDegree() const
{
	return mDegree;
}

void PolynomialCalculator::ClearPolynomial(Polynomial& outPolynomial)
{
	outPolynomial.mDegree = mDegree;

	using namespace DirectX;
	XMVECTOR z = XMVectorZero();
	for (int powerX = 0; powerX <= mDegree; ++powerX)
	{
		for (int powerY = mDegree - powerX; powerY >= 0; --powerY)
		{
			outPolynomial.mA[powerX][powerY] = z;
		}
	}
}

void PolynomialCalculator::UpdateThePolynomialWithPixel(Polynomial& outPolynomial,
	int inX, int inY, COLORREF inColor)
{
	using namespace DirectX;
	XMVECTOR color = XMVectorSet(GetRValue(inColor), GetGValue(inColor), GetBValue(inColor), 0);

	for (int powerX = 0; powerX <= mDegree; ++powerX)
	{
		float deltaForX = mDeltaF[powerX][inX];
		for (int powerY = mDegree - powerX; powerY >= 0; --powerY)
		{
			outPolynomial.mA[powerX][powerY] += 
				(deltaForX * mDeltaF[powerY][inY]) * color;
		}
	}
}